<!doctype html>
<html>
<head>
    <title>Javascript LatticeMico32 Emulator (runs Linux)</title>
    <style>
    .term {
        font-family: courier,fixed,swiss,monospace,sans-serif;
        font-size: 14px;
        color: #f0f0f0;
        background: #000000;
    }

    .termReverse {
        color: #000000;
        background: #00ff00;
    }
    </style>
    <!-- third party -->
    <script src="third_party/term.js"></script>

    <!-- lm32 -->
    <script src="lib/lm32_base.js"></script>
    <script src="lib/lm32_fb.js"></script>
    <script src="lib/lm32_pic.js"></script>
    <script src="lib/lm32_cpu_dynrec.js"></script>
    <script src="lib/lm32_cpu_interp.js"></script>
    <script src="lib/lm32_bus.js"></script>
    <script src="lib/lm32_ram.js"></script>
    <script src="lib/lm32_hwsetup.js"></script>
    <script src="lib/lm32_sys.js"></script>
    <script src="lib/lm32_timer.js"></script>
    <script src="lib/lm32_uart.js"></script>

    <script>
        "use strict";
        var lm32_debug_terminal = false;
        var terminal;
        var sys;
        var worker;
        var worker_pending_str = false;
        var ics; // instruction count state


        function update_mips(mips) {
            var mspan = document.getElementById('mips');
            mspan.textContent = mips.toFixed(2).toString();
        }

        function ics_init() {
            ics = {};
            ics.instr_count = 0;
            ics.instr_count_start = (new Date()).getTime();
        }

        function ics_update(steps) {
            ics.instr_count += steps;
            if (ics.instr_count >= 10000000) {
                var time = (new Date()).getTime();
                var delta = time - ics.instr_count_start;
                ics.instr_count_start = time;
                ics.instr_count = 0;
                update_mips(10000.0/delta);
            }
        }

        function step_forever() {
            var requested = 50000;
            var stepped = sys.step(requested);
            ics_update(stepped);
            setTimeout(step_forever, 0);
        }

        function terminal_putchar(c) {
            if(lm32_debug_terminal) {
                var ta = document.getElementById('text_mode');
                if(c == 8) {
                    ta.textContent = ta.textContent.substring(0, ta.textContent.length - 1);
                } else {
                    ta.textContent += String.fromCharCode(c);
                }
                ta.scrollTop = ta.scrollHeight;
            }
            terminal.write(String.fromCharCode(c));
        }

        function lm32_start_evr(kernel_filename) {
            var on_start_evr_result = function(result) {
                if(result.success) {
                    sys = result.system;
                    ics_init();
                    setTimeout(step_forever, 0);
                } else {
                    terminal.write(
                        "ERROR: can't start " +
                        kernel_filename +
                        "\r\n"
                    );
                }
            }
            lm32.start_evr(
                terminal_putchar,
                kernel_filename,
                on_start_evr_result
            );
        }


        function lm32_start_regular() {
            terminal.write("Loading...\r\n");
            var on_start_uclinux_result = function(result) {
                if(result.success) {
                    sys = result.system;
                    ics_init();
                    setTimeout(step_forever, 0);
                } else {
                    terminal.write("ERROR: Can't load uClinux.\r\n")
                }
            }
            lm32.start_uclinux(
                terminal_putchar,
                '../linux/vmlinux.bin',
                '../linux/romfs.ext2',
                on_start_uclinux_result
            );

        }

        function lm32_receive_worker_message(e) {
            var msg = e.data;
            var type = msg.type;
            switch(type) {
                case 'worker_started':
                    ics_init();
                    worker.postMessage({type: 'work'});
                    break;

                case 'work_done':
                    if(worker_pending_str !== false) {
                        var payload = worker_pending_str;
                        worker_pending_str = false;
                        worker.postMessage(
                            {
                                type: 'terminal_send_str',
                                payload: payload
                            }
                        );
                    }
                    worker.postMessage({type: 'work'});

                    // keep ics_update after postMessage so cpu can work
                    // while DOM is being updated
                    ics_update(msg.instructions);
                    break;

                case 'terminal_putchar':
                    terminal_putchar(msg.payload);
                    break;

                case 'inform_mips':
                    inform_mips(msg.payload);
                    break;

                default:
                    throw({error: 'Unknown message', msg: msg});
                    break;
            }
        }

        function lm32_start_worker() {
            terminal.write("Loading with Web Worker...\r\n")
            window.worker = new Worker('lib/lm32_worker.js');
            worker.onmessage = lm32_receive_worker_message;

            function terminal_send_str(str) {
                if(worker_pending_str === false) {
                    worker_pending_str = '';
                }
                worker_pending_str += str;
            }
            window.sys = {
                console_send_str: terminal_send_str,
            };
            worker.postMessage(
                {
                    type: 'lm32_start',
                    kernel_url: '../../linux/vmlinux.bin',
                    romfs_url: '../../linux/romfs.ext2',
                }
            );
        }

        function start_terminal() {
            function term_handler(s) {
            if (typeof sys != 'undefined') {
                    sys.console_send_str(s);
                } else {
                    console.log('Terminal started but no sys yet.');
                }
            }

            if(lm32_debug_terminal) {
                var ta = document.createElement('textarea');
                ta.id ='text_mode';
                ta.rows=25;
                ta.cols=80;
                document.body.appendChild(ta);
            }

            terminal = new Term(80, 25, term_handler);
            terminal.open();
            terminal.write('Click above to load.\r\n');
        }
    </script>

</head>
<body>
<p> Copyright (c) 2011-2012, 2016-2017 Reginaldo Silva </p>
<p> Contact: reginaldo at ubercomp </p>
<button onclick="lm32_start_regular()">Start!</button>
<button onclick="lm32_start_worker()">Start with web workers!</button>
<p>Login: root ; Password: lattice</p>
<div>Processor real time speed: <span id="mips">0.00</span> MIPS</div>
<div id="frameBuffer"></div>
<div id="vt100Div"></div>
<script type="text/javascript">
    start_terminal();
</script>
</body>
</html>
